/*	--- 创建Stream ---


要使用Stream，就必须现创建它。

创建Stream有很多种方法，我们来一一介绍。


========================================


#	1. Stream.of()
 

创建Stream最简单的方式是直接用【Stream.of()静态方法】，

传入【可变参数】即创建了一个【能输出确定元素的Stream】：*/
import java.util.stream.Stream;

public class Main {
	public static void main(String[] args) {
		Stream<String> stream = Stream.of("A", "B", "C", "D");

		// forEach()方法 相当于 内部循环调用
		// 可传入符合Consumer接口的void accept(T t)的方法引用
		stream.forEach(System.out::println);
	}
}

/*
虽然这种方式基本上没啥实质性用途，但测试的时候很方便。


===============


#	2. 基于 数组 / Collection 


第二种创建Stream的方法是基于一个 数组/Collection(容器)

这样情况创建的Stream, 输出的元素就是【数组】或者【Collection(容器)持有的元素】：*/
import java.util.*;
import java.util.stream.*;

public class Main {
	public static void main(String[] args) {
		Stream<String> stream1 = Arrays.stream(new String[] {
			"A", "B", "C"
		});

		Stream<String> stream2 = List.of(
			"X", "Y", "Z"
		).stream();

		stream1.forEach(System.out::println);
		stream2.forEach(System.out::println);
	}
}

/*
把数组变成Stream使用Arrays.stream()方法。

对于Collection(容器)（List、Set、Queue等 implements Collection ），直接调用stream()方法就可以获得Stream。

上述【创建Stream的方法】都是把一个现有的序列变为Stream，它的元素是固定的。


======================================


#	3. 基于Supplier


创建Stream还可以通过 Stream.generate()方法，它需要传入一个【Supplier对象】：*/
Stream<String> s = Stream.generate(Supplier<String> sp);


/*
基于Supplier创建的Stream会不断调用Supplier.get()方法来不断产生下一个元素，这种Stream保存的不是元素，而是【算法】，它可以用来表示【无限序列】。

例如，我们编写一个【能不断生成自然数的Supplier】，它的代码非常简单，每次【调用get()方法】，就生成【下一个自然数】：*/
import java.util.function.*;
import java.util.stream.*;

public class Main {
	public static void main(String[] args) {
		Stream<Integer> natual = Stream.generate(new NatualSupplier());

		// 注意: 无线序列必须先编程有限序列,才能打印
		natual.limit(20).forEach(System.out::println);
	}
}

class NatualSupplier implements Supplier<Integer> {
	int n = 0;

	public Integer get() {
		n++;
		return n;
	}
}

/*

上述代码我们用一个【Supplier<Integer>】模拟了一个无限序列（当然受int范围限制不是真的无限大）。

如果用【List】表示，即便在int范围内，也会占用巨大的内存，而【Stream】几乎不占用空间，因为每个元素都是实时计算出来的，用的时候再算。

对于【无限序列】，如果直接调用forEach()或者count()这些最终求值操作，会进入死循环，因为永远无法计算完这个序列，所以正确的方法是先把无限序列变成有限序列，例如，用limit()方法可以截取前面若干个元素，这样就变成了【一个有限序列】，对这个有限序列调用forEach()或者count()操作就没有问题。


=============================================


#	4. 其他方法


【创建Stream】的【第3种方法】是通过一些【API提供的接口】，直接获得【Stream】。

例如，【Files类的lines()方法】可以把【一个文件】变成【一个Stream】，【每个元素】代表【文件的一行内容】：*/
try (Stream<String> lines = File.lines(Path.get("/path/to/file.txt"))) {
	//...
}


/*
此方法对于按行遍历文本文件十分有用。

另外，【正则表达式的Pattern对象】有一个【splitAsStream()方法】，可以直接把【一个长字符串】分割成【Stream序列】而不是数组：*/
Pattern p = Pattern.compile("\\s+");
Stream<String> s = p.splitAsStream("The quick brown fox jumps over the lazy dog.");
s.forEach(System.out::println);



/*===================================================


#	5. 基本类型


因为【Java的范型】不支持【基本类型】，所以我们无法用【Stream<int>】这样的类型，会发生编译错误。

为了保存int，只能使用【Stream<Integer>】，但这样会产生【频繁的装箱、拆箱操作】。

为了提高效率，Java标准库提供了 【IntStream、LongStream、DoubleStream】 这三种【使用基本类型的Stream】，它们的使用方法和【范型Stream】没有大的区别，设计【这三个Stream】的目的是【提高运行效率】：*/
IntStream is = Arrays.stream(new int[] { 1,2,3 });
	// 将int[]数组 变为 IntStream

LongStream ls = Arrays.of("1", "2", "3").stream().mapToLong(Long::parseLong);
	// 将Stream<String> 转化为 LongStream



/*=============================================================


#	----- 创建Stream の 小结 ----- 


1. 创建Stream的方法有 ：
	· 通过【指定元素、指定数组、指定Collection】创建 Stream；
	· 通过【Supplier】创建 Stream，可以是无限序列；
	· 通过【其他类的相关方法】创建 Stream。

2. 【基本类型的Stream】有
	· IntStream
	· LongStream
	· DoubleStream。

	· IntStream
		IntStream is = Arrays.stream(new int[] { 1, 2, 3 });

3. Stream 可以通过 new Supplier() 的 Supplier.get() 制定 Stream.generate()规则

4. 通过一些API提供的接口，直接获得Stream
	例如：
	按行遍历文本文件
		Stream<String> lines = Files.lines(Paths.get("/path/file.txt"))
	
	正则表达式的Pattern对象 有一个【splitAsStream()方法】
		Pattern p = Pattern.compile("\\s+");
		Stream<String> s = p.splitAsStream("The quick brown fox");


=================================

Supplier示例: */
class NatualSupplier implements Supplier<Integer> {
    int n = 0;

    public Integer get() {
        n++;
        return n;
    }
}

public class Main {
    public static void main(String[] args) {
        Stream<Integer> natual = Stream.generate(new NatualSupplier());
        // 注意：无限序列必须先变成有限序列再打印:
        natual.limit(20).forEach(System.out::println);
    }
}

